package vip.linhs.stock.scheduled;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import vip.linhs.stock.api.TradeResultVo;
import vip.linhs.stock.api.request.GetAssetsRequest;
import vip.linhs.stock.api.request.GetDealDataRequest;
import vip.linhs.stock.api.request.GetOrdersDataRequest;
import vip.linhs.stock.api.request.RevokeRequest;
import vip.linhs.stock.api.request.SubmitRequest;
import vip.linhs.stock.api.response.GetAssetsResponse;
import vip.linhs.stock.api.response.GetDealDataResponse;
import vip.linhs.stock.api.response.GetOrdersDataResponse;
import vip.linhs.stock.api.response.RevokeResponse;
import vip.linhs.stock.api.response.SubmitResponse;
import vip.linhs.stock.exception.UnauthorizedException;
import vip.linhs.stock.model.po.DailyIndex;
import vip.linhs.stock.model.po.ExecuteInfo;
import vip.linhs.stock.model.po.Task;
import vip.linhs.stock.service.HolidayCalendarService;
import vip.linhs.stock.service.StockCrawlerService;
import vip.linhs.stock.service.TaskService;
import vip.linhs.stock.service.TradeApiService;
import vip.linhs.stock.util.StockUtil;

@Component
public class ScheduledTasks {

    private final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class);

    @Autowired
    private HolidayCalendarService holidayCalendarService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private TradeApiService tradeApiService;

    /**
     * begin of year
     */
    @Scheduled(cron = "0 0 0 1 1 ?")
    public void runBeginOfYear() {
        try {
            List<ExecuteInfo> list = taskService.getPendingTaskListById(Task.BeginOfYear.getId());
            executeTask(list);
        } catch (Exception e) {
            logger.error("task runBeginOfYear error", e);
        }
    }

    /**
     * begin of day
     */
    @Scheduled(cron = "0 0 6 ? * MON-FRI")
    public void runBeginOfDay() {
        boolean isBusinessTime = holidayCalendarService.isBusinessDate(new Date());
        if (!isBusinessTime) {
            return;
        }
        try {
            List<ExecuteInfo> list = taskService.getPendingTaskListById(Task.BeginOfDay.getId());
            executeTask(list);
        } catch (Exception e) {
            logger.error("task runBeginOfDay error", e);
        }
    }

    /**
     * update of stock
     */
    @Scheduled(cron = "0 0 9 ? * MON-FRI")
    public void runUpdateOfStock() {
        boolean isBusinessTime = holidayCalendarService.isBusinessDate(new Date());
        if (!isBusinessTime) {
            return;
        }
        try {
            List<ExecuteInfo> list = taskService.getPendingTaskListById(Task.UpdateOfStock.getId());
            executeTask(list);
        } catch (Exception e) {
            logger.error("task runUpdateOfStock error", e);
        }
    }

    /**
     * update of daily index
     */
    @Scheduled(cron = "0 0 17,18,19 ? * MON-FRI")
    public void runUpdateOfDailyIndex() {
        boolean isBusinessTime = holidayCalendarService.isBusinessDate(new Date());
        if (!isBusinessTime) {
            return;
        }
        try {
            List<ExecuteInfo> list = taskService.getPendingTaskListById(Task.UpdateOfDailyIndex.getId());
            executeTask(list);
        } catch (Exception e) {
            logger.error("task runUpdateOfDailyIndex error", e);
        }
    }

    /**
     * ticker
     */
    @Scheduled(cron = "0,15,30,45 * 9,10,11,13,14 ? * MON-FRI")
    public void runTicker() {
        boolean isBusinessTime = holidayCalendarService.isBusinessTime(new Date());
        if (!isBusinessTime) {
            return;
        }

        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);
        if (hour == 14 && minute >= 55) {
            return;
        }

        try {
            List<ExecuteInfo> list = taskService.getPendingTaskListById(Task.Ticker.getId(), Task.TradeTicker.getId());
            executeTask(list);
        } catch (Exception e) {
            logger.error("task runTicker error", e);
        }
    }

    /**
     * apply new stock
     */
    @Scheduled(cron = "0 1 10,14 ? * MON-FRI")
    public void applyNewStock() {
        boolean isBusinessTime = holidayCalendarService.isBusinessTime(new Date());
        if (!isBusinessTime) {
             return;
        }

        try {
            List<ExecuteInfo> list = taskService.getPendingTaskListById(Task.ApplyNewStock.getId());
            executeTask(list);
        } catch (Exception e) {
            logger.error("task applyNewStock error", e);
        }
    }

    private void executeTask(List<ExecuteInfo> list) {
        for (ExecuteInfo executeInfo : list) {
            taskService.executeTask(executeInfo);
        }
    }

    @Scheduled(cron = "0 0,20,40 * ? * MON-FRI")
    public void heartbeat() {
        boolean isBusinessDate = holidayCalendarService.isBusinessDate(new Date());
        if (!isBusinessDate) {
            return;
        }
        TradeResultVo<GetAssetsResponse> tradeResultVo = null;
        try {
            tradeResultVo = tradeApiService.getAsserts(new GetAssetsRequest(1));
        } catch (UnauthorizedException e) {
            logger.error("Unauthorized", e);
        }
        if (tradeResultVo == null || !tradeResultVo.isSuccess()) {
            if (tradeResultVo != null) {
                logger.error("heartbeat: {}", tradeResultVo.getMessage());
            }
            boolean isBusinessTime = holidayCalendarService.isBusinessTime(new Date());
            if (isBusinessTime) {
                try {
                    List<ExecuteInfo> list = taskService.getPendingTaskListById(Task.AutoLogin.getId());
                    executeTask(list);
                    logger.info("auto login");
                } catch (Exception e) {
                    logger.error("task AutoLogin error", e);
                }
            }

        }
    }

    @Autowired
    private StockCrawlerService stockCrawlerService;

    /**
     * ticker
     */
    @Scheduled(cron = "0,30 * 9,10,11,13,14 ? * MON-FRI")
    public void runAutoSubmit() {
        boolean isBusinessTime = holidayCalendarService.isBusinessTime(new Date());
        if (!isBusinessTime) {
//            return;
        }

        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);
        if (hour == 14 && minute >= 55) {
            if (minute >= 57) {
                return;
            }
            TradeResultVo<GetOrdersDataResponse> ordersData = tradeApiService.getOrdersData(new GetOrdersDataRequest(1));

            String revokes = ordersData.getData().stream().filter(v -> v.getWtzt().equals(GetOrdersDataResponse.YIBAO) && SubmitRequest.B.equals(v.getMmlb()))
              .map(v -> String.format("%s_%s", DateFormatUtils.format(new Date(), "yyyyMMdd"), v.getWtbh()))
              .collect(Collectors.joining(","));

            if (revokes.length() == 0) {
                return;
            }

            RevokeRequest request = new RevokeRequest(1);
            request.setRevokes(revokes);
            logger.info("trade request: {}", request);
            TradeResultVo<RevokeResponse> tradeResultVo = tradeApiService.revoke(request);
            logger.info("trade response: {}", tradeResultVo);
            return;
        }

        try (BufferedReader in = new BufferedReader(new FileReader("/data/stock_list.txt"))) {
            String line;
            while ((line = in.readLine()) != null) {
                if (line.length() == 0 || !line.startsWith("code")) {
                    continue;
                }

                line = line.trim().replaceAll("\\s+", " ");
                String[] arr = line.split(" ");

                String stockCode = arr[0].trim().split(":")[1];
                BigDecimal price = new BigDecimal(arr[1].trim().split(":")[1]);

                TradeResultVo<GetDealDataResponse> dealData = tradeApiService.getDealData(new GetDealDataRequest(1));
                if (dealData.getData().stream().anyMatch(v -> v.getZqdm().equals(stockCode))) {
                    continue;
                }

                TradeResultVo<GetOrdersDataResponse> ordersData = tradeApiService.getOrdersData(new GetOrdersDataRequest(1));
                if (ordersData.getData().stream().anyMatch(v -> v.getZqdm().equals(stockCode))) {
                    continue;
                }

                DailyIndex dailyIndex = stockCrawlerService.getDailyIndex(StockUtil.getFullCode(stockCode));
                price = price.min(dailyIndex.getClosingPrice()).multiply(new BigDecimal("0.98")).setScale(2, BigDecimal.ROUND_HALF_UP);
                int amount = (int)(8000 / price.doubleValue() / 100) * 100;
                if (amount == 0) {
                    amount = 100;
                }

                SubmitRequest submitRequest = new SubmitRequest(1);
                submitRequest.setAmount(amount);
                submitRequest.setPrice(price.doubleValue());
                submitRequest.setTradeType(SubmitRequest.B);
                submitRequest.setStockCode(stockCode);
                logger.info("trade request: {}", submitRequest);
                TradeResultVo<SubmitResponse> tradeResultVo = tradeApiService.submit(submitRequest);
                logger.info("trade response: {}", tradeResultVo);

                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        } catch (IOException e) {
            // ignore
        }

    }

}
